<script setup>
import {onMounted, onUnmounted, ref, watch} from 'vue'
import {Plus, QuestionFilled, Upload, MessageBox} from "@element-plus/icons-vue";
import requestUtil from "@/utils/request";
import {ElMessage, ElMessageBox} from "element-plus";
import destHostDialog from "@/views/fileDistribution/components/hostDialog.vue";
import srcHostDialog from "@/views/fileDistribution/components/hostDialog.vue";
import Cookies from "js-cookie";
import router from "@/router";
import CryptoJS from "crypto-js"

const tableData = ref([])

const destPath = ref('')

const total = ref(0)

const destHostDialogVisible = ref(false)
const destHostDialogTitle = ref("")

const srcHostDialogVisible = ref(false)
const srcHostDialogTitle = ref("")

const handleDestHostDialogValue = () => {
  // srcHostTuple.value = null
  dialogStatus.value = 1
  destHostDialogTitle.value = '选择目标主机'
  destHostDialogVisible.value = true
}
const handleSrcHostDialogValue = () => {
  srcStatus.value = 1
  fileList.value = []
  dialogStatus.value = 2
  srcHostDialogTitle.value = '选择源主机'
  srcHostDialogVisible.value = true
}

const initTableData = async () => {
  try {
    const res = await requestUtil.get('host/file/distribution')
    const data = res.data
    if (data.code === 200) {
      tableData.value = data.msg
      total.value = data.total
    } else {
      ElMessage.error(data.msg)
    }
  } catch (error) {
    console.error('Error fetching table data:', error)
    ElMessage.error('请求失败，请稍后再试')
  }
}
initTableData()

// 定义接收到的元组
const destHostTuple = ref([]);
const srcHostTuple = ref(null);

// const outPut = ref("")

const fileList = ref([]);


// 处理子组件传递过来的元组
const destHandleTuple = (tuple) => {
  console.log(tuple, '=========tuple dest')
  destHostTuple.value = tuple;
};
const srcHandleTuple = (tuple) => {
  console.log(tuple, '=========tuple src')
  srcHostTuple.value = tuple.value;
  srcStatus.value = 1;
  fileList.value.push(tuple.value)
};

const headers = ref({
  Authorization: Cookies.get('token')
})
const uploadRef = ref(null)
const uploadAction = ref('')
const uploadActionData = ref({
  host_list: [],
  path: '',
  op_type: 'upload',
})

const handleFileChange = (file) => {
  // 仅当文件状态为ready时添加（避免重复添加）
  if (file.status === 'ready') {
    fileList.value.push(file)
  }
}

const submitUpload = async () => {
  // eslint-disable-next-line no-async-promise-executor
  return new Promise(async (resolve, reject) => {
    try {
      uploadActionData.value.host_list = destHostTuple.value;
      uploadActionData.value.path = destPath.value;
      const destHostTupleForm = []
      if (srcStatus.value) {
        // 该if为rsync源主机到目标主机的分发代码
        let ws = null;
        for (const item of destHostTuple.value) {
          destHostTupleForm.push(item.id)
          const task = {
            fileName: destPath.value,
            host: item,
            progress: 0,
            speed: 0,
            loaded: 0,
            lastTime: Date.now(),
            status: '', // 改为空字符串而不是 uploading
            remoteProgress: 0,
            ws: null,
          }
          uploadTasks.value.push(task)
          ws = await connectServer(destPath.value, item.id, 1);
          task.ws = ws; // 存储WebSocket实例
        }

        const formData = {
          'dest_host_list': destHostTupleForm,
          'src_host_id': srcHostTuple.value['id'],
          'dest_path': destPath.value,
          'src_path': srcPath.value
        }

        const res = await requestUtil.post('/host/file/srcToDest', formData)
        const data = res.data
        if (data.code === 200) {
          ElMessage.success("分发成功")
        } else {
          ElMessage.error(data.msg)
        }

        await initTableData();
      } else {
        // 此为上传文件的分发代码
        if (fileList.value.length === 0) {
          ElMessage.warning('请先选择文件');
          return;
        }
        if (uploadActionData.value.host_list.length === 0) {
          ElMessage.warning('请先添加主机');
          return;
        }

        if (uploadActionData.value.path === '') {
          ElMessage.warning('请先添加目标路径');
          return;
        }
        uploadRef.value.submit();
      }
      resolve()
    } catch (error) {
      console.error('Error fetching table data:', error)
      ElMessage.error('请求失败，请稍后再试')
      reject(error)
    }
  })
}
const deleteFile = (index) => {
  fileList.value.splice(index, 1);
};


// 新增的响应式数据
const uploadTasks = ref([])

// 速度格式化方法
const formatSpeed = (speed) => {
  if (speed === 0) return '0 B/s'
  const units = ['B/s', 'KB/s', 'MB/s']
  let i = 0
  while (speed >= 1024 && i < units.length - 1) {
    speed /= 1024
    i++
  }
  return `${speed.toFixed(1)} ${units[i]}`
}

// 新增的上传逻辑（替换原来的uploadFile）
const uploadFile = async ({file}) => {
  const tasks = destHostTuple.value.map(host => {
    const task = {
      fileName: file.name,
      host,
      progress: 0,
      speed: 0,
      loaded: 0,
      lastTime: Date.now(),
      status: '', // 改为空字符串而不是 uploading
      remoteProgress: 0,
      ws: null,
    }
    uploadTasks.value.push(task)
    return uploadToHost(file, host, task)
  })

  try {
    await Promise.all(tasks)
  } catch (error) {
    console.error('上传出错:', error)
  } finally {
    initTableData()
    // 5秒后清理完成的任务
    setTimeout(() => {
      uploadTasks.value = uploadTasks.value.filter(t => t.status === 'exception')
    }, 5000)
  }
}

function calculateMD5(str) {
  return CryptoJS.MD5(str).toString(CryptoJS.enc.Hex);
}

const connectServer = (file, hostParam, status = 0) => {
  const hostID = status ? hostParam : hostParam.id;
  return new Promise((resolve, reject) => {
    try {
      let md5Txt = ''
      if (!status) {
        md5Txt = file.name + hostParam.id
      } else {
        md5Txt = file + hostParam
      }
      const md5Value = calculateMD5(md5Txt);
      // 获取域名
      const domain = window.location.hostname;
      // 获取端口
      const port = window.location.port;
      // 建立WebSocket连接
      const wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${domain}:${port}${requestUtil.getServerUrl()}ws/${md5Value}`

      const ws = new WebSocket(wsUrl);

      ws.onopen = () => {
        resolve(ws);
      };

      ws.onerror = (error) => {
        reject(error);
      };

      ws.onmessage = (e) => {
        try {
          const message = JSON.parse(e.data);
          switch (message.type) {
            case 'progress':
              uploadTasks.value.forEach(item => {
                if (status) {
                  if (item.fileName === destPath.value && item.host.id === hostID) {
                    item.remoteProgress = message.data;
                    if (message.data === 100) {
                      // 主动关闭WebSocket，触发onclose
                      ws.close();
                    }

                    const now = Date.now()
                    const timeDiff = now - item.lastTime

                    // 强制更新速度（至少50ms更新）
                    if (timeDiff > 50) {
                      item.lastTime = now
                    }
                  }
                } else {
                  if (item.fileName === file.name && item.host.id === hostID) {
                    item.remoteProgress = message.data;
                    if (message.data === 100) {
                      ws.close();
                    }
                  }
                }
              });
              break;
          }
        } catch (error) {
          console.error('消息解析错误:', error);
        }
      };

      ws.onclose = () => {
        console.log('WebSocket连接关闭');
        const taskIndex = uploadTasks.value.findIndex(t => t.ws === ws);
        if (taskIndex !== -1) {
          const task = uploadTasks.value[taskIndex];
          if (task.remoteProgress === 100) {
            uploadTasks.value.splice(taskIndex, 1);
          }
        }
      };

    } catch (error) {
      reject(error);
    }
  });
};

// 单独的上传方法（每个主机单独上传）
const uploadToHost = (file, host, task) => {
  return new Promise(async (resolve, reject) => {
    let ws = null;
    try {
      const formData = new FormData()
      formData.append('files', file)
      formData.append('host_list', host.id)
      formData.append('path', uploadActionData.value.path)
      ws = await connectServer(file, host)
      task.ws = ws; // 存储WebSocket实例

      const res = await requestUtil.post('/host/file/distribution', formData, {
        headers: {'Content-Type': 'multipart/form-data'},
        onUploadProgress: (progressEvent) => {
          const now = Date.now()
          const timeDiff = now - task.lastTime
          const loadedDiff = progressEvent.loaded - task.loaded

          // 强制更新速度（至少50ms更新）
          if (timeDiff > 50) {
            task.speed = loadedDiff / (timeDiff / 1000)
            task.lastTime = now
            task.loaded = progressEvent.loaded
          }

          // 处理total为0的情况
          const total = progressEvent.total > 0 ? progressEvent.total : file.size
          if (total > 0) {
            task.progress = Math.min(
                100,
                Math.round((progressEvent.loaded * 100) / total)
            )
          }

          // 强制触发视图更新
          uploadTasks.value = [...uploadTasks.value]
        }
      })
      const data = res.data
      if (data.code === 200) {
        await initTableData()
        // ElMessage.success(data.msg)
        uploadTasks.value.forEach((item, index, array) => {
          if (item.fileName === file.name && item.host.id === host.id) {
            array.splice(index, 1); // 删除符合条件的元素
          }
        });
      } else {
        ElMessage.error(`${file.name} ${host.hostname} ${data.msg}`)
      }

      task.status = 'success'
      resolve()
    } catch (error) {
      task.status = 'exception'
      task.speed = 0
      reject(error)
    } finally {
      // 无论成功失败都关闭WebSocket
      if (ws && ws.readyState === WebSocket.OPEN) {
        ws.close();
      }
    }
  })
}

const handleBeforeUnload = (event) => {
  // 如果有文件正在传输，弹出提醒
  if (fileList.value.length !== 0) {
    event.preventDefault();
    event.returnValue = ''; // 必须设置 returnValue 才能触发提醒
  }
};

const handleRouteChange = (to, from, next) => {
  if (fileList.value.length > 0) {
    // 使用自定义弹窗替代原生confirm
    ElMessageBox.confirm('当前有未完成的上传任务，确定要离开吗？', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(() => {
      fileList.value = []
      next()
    }).catch(() => {
      next(false)
    })
  } else {
    next()
  }
}

const srcStatus = ref(0)
const srcPath = ref('')
const dialogStatus = ref(0) // 1：目标主机、2：源主机、3：批量执行
const changeSrcStatus = () => {
  fileList.value = []
  srcStatus.value = 0;
}

onMounted(() => {
  // 添加路由守卫
  router.beforeEach((to, from, next) => {
    handleRouteChange(to, from, next)
  })
  window.addEventListener('beforeunload', handleBeforeUnload);
});

onUnmounted(() => {
  window.removeEventListener('beforeunload', handleBeforeUnload);
});

watch(
    () => uploadTasks.value.length,
    () => {
      if (uploadTasks.value.length === 0) {
        // 处理上传成功的逻辑
        ElMessage.success('所有主机上传完成')
        fileList.value = []
        uploadActionData.value.host_list = []
        destHostTuple.value = []
        // uploadRef.value.clearFiles(); // 清空文件列表
        // uploadRef.value = ''

        // 清空el-upload组件内部文件列表
        if (uploadRef.value) {
          uploadRef.value.clearFiles()
        }
        if (destPath.value) {
          destPath.value = ''
        }
        if (srcPath.value) {
          srcPath.value = ''
        }
      }
    }
)
</script>

<template>
  <div class="app-container">
    <el-row :gutter="20">
      <el-col :span="14">
        <el-card class="box-card-hard">
          <template v-slot:header>
            <div class="space-between">
              <el-text>
                <el-text style="color: red">*</el-text>
                数据源
                <el-tooltip raw-content content="linux文件分发功能依赖 sftp、sshpass 两个命令<br>
                如果未安装，请在命令批量执行中安装此命令" placement="right">
                  <el-icon>
                    <QuestionFilled style="color: red"/>
                  </el-icon>
                </el-tooltip>
              </el-text>

              <el-row style="margin-top: 10px;margin-bottom: 10px">
                <div>
                  <el-upload
                      name="files"
                      :headers="headers"
                      :http-request="uploadFile"
                      multiple
                      ref="uploadRef"
                      :action="uploadAction"
                      :auto-upload="false"
                      :show-file-list="false"
                      :on-change="handleFileChange"
                      :data="uploadActionData"
                  >
                    <template #trigger>
                      <el-button :icon="Upload" @click="changeSrcStatus()" type="primary">上传本地文件</el-button>
                    </template>
                  </el-upload>
                </div>
                <div style="margin-left: 10px">
                  <el-button @click="handleSrcHostDialogValue()" size="default" :icon="Plus" type="primary">
                    添加源主机文件
                  </el-button>
                </div>
              </el-row>
            </div>
          </template>

          <!--          <el-table :show-header="false" :data="fileList" style="width: 100%;height: 396px;margin-bottom: 50px">-->
          <el-table :data="fileList" style="width: 100%;height: 377px;margin-bottom: 50px">
            <template v-if="!srcStatus">
              <el-table-column prop="name" label="文件名"/>
              <el-table-column label="操作" width="120">
                <template #default="scope">
                  <el-button
                      type="danger"
                      size="small"
                      @click="deleteFile(scope.$index)"
                  >删除
                  </el-button>
                </template>
              </el-table-column>
            </template>
            <template v-else>
              <el-table-column prop="hostname" label="主机名" width="260"/>
              <el-table-column label="源文件路径" width="500">
                <el-input
                    v-model="srcPath"
                    style="width: 450px"
                    placeholder="请输入需要同步的源目录路径"
                />
              </el-table-column>
              <el-table-column label="操作" width="120">
                <template #default="scope">
                  <el-button
                      type="danger"
                      size="small"
                      @click="deleteFile(scope.$index)"
                  >删除
                  </el-button>
                </template>
              </el-table-column>
            </template>
            <!-- 新增 empty 插槽 -->
            <template #empty>
              <div class="empty-container">
                <el-icon :size="60" color="#c0c4cc" class="empty-icon">
                  <MessageBox/>
                </el-icon>
                <div class="empty-text">暂未选择文件</div>
              </div>
            </template>
          </el-table>

          <div style="margin-bottom: 20px">
            <el-text>
              分发目标
            </el-text>
          </div>
          <el-form>
            <div class="space-between" style="margin-bottom: 30px">
              <el-text>
                <el-tooltip raw-content content="若源主机和目标主机均为windows，则只能传输单个文件" placement="right">
                  <el-icon>
                    <QuestionFilled style="color: red"/>
                  </el-icon>
                </el-tooltip>
                目标路径:
              </el-text>
              <el-input
                  v-model="destPath"
                  style="width: 90%"
                  :rows="25"
                  placeholder="请输入目标路径"
              />
            </div>
            <div class="space-between" style="margin-bottom: 20px">
              <el-text style="margin-right: 20px">
                <el-text style="color: red">*</el-text>
                目标主机:
              </el-text>
              <div style="width: 90%">
                <el-button @click="handleDestHostDialogValue()" size="default" :icon="Plus" type="primary">
                  添加目标主机
                </el-button>
                <el-text>
                  &nbsp;&nbsp;已选择
                  <el-text style="color: #3e9dff;font-size: 20px;font-weight:bold"> {{ destHostTuple.length }}</el-text>
                  台主机
                </el-text>
              </div>
            </div>
            <el-button @click="submitUpload()" size="default" :icon="Plus" type="danger"
                       style="margin-top: 10px">
              开始分发
            </el-button>
          </el-form>
        </el-card>
      </el-col>

      <el-col :span="10">
        <el-card class="box-card-hard">
          <template v-slot:header>
            <div class="space-between">
              <div>
                <el-text>
                  分发记录
                  <el-tooltip content="自动清理，保留最近100条记录。" placement="right">
                    <el-icon>
                      <QuestionFilled/>
                    </el-icon>
                  </el-tooltip>
                </el-text>

              </div>
            </div>
          </template>
          <el-table :data="tableData" style="width: 100%" height="600px">
            <el-table-column align="center" prop="user" label="分发用户" width="80"/>
            <el-table-column align="center" prop="hostname" label="主机" width="200"/>
            <el-table-column align="center" prop="path" label="分发路径" width="260"/>
            <el-table-column align="center" prop="status" width="100" label="分发状态">
              <template v-slot="scope">
                <span v-if="scope.row.status === 1" class="el-tag el-tag--success">成功</span>
                <span v-else-if="scope.row.status === 2" class="el-tag el-tag--danger">失败</span>
              </template>
            </el-table-column>
            <el-table-column align="center" prop="distribution_time" label="分发时间" width="200"/>
          </el-table>
        </el-card>
      </el-col>
    </el-row>

    <destHostDialog v-model="destHostDialogVisible"
                    :dialogVisible="destHostDialogVisible"
                    :dialogStatus="dialogStatus"
                    @sendTuple="destHandleTuple"
                    :dialogTitle="destHostDialogTitle"></destHostDialog>

    <srcHostDialog v-model="srcHostDialogVisible"
                   :dialogVisible="srcHostDialogVisible"
                   :dialogStatus="dialogStatus"
                   @sendTuple="srcHandleTuple"
                   :dialogTitle="srcHostDialogTitle"></srcHostDialog>

    <el-card
        class="progress-window"
        v-show="uploadTasks.length > 0"
    >
      <template #header>
        <div class="space-between">
          <span>文件传输进度</span>
          <el-tag type="info">正在传输 {{ uploadTasks.length }} 个任务</el-tag>
        </div>
      </template>
      <el-scrollbar max-height="500">
        <div
            v-for="(task, index) in uploadTasks"
            :key="index"
            class="progress-item"
        >
          <el-row class="progress-header">
            <el-text class="filename">{{ task.fileName }}</el-text>
            <el-text type="info" class="hostname">@ {{ task.host.hostname }}</el-text>
            <el-text v-if="!srcStatus" type="primary" class="speed">{{ formatSpeed(task.speed) }}</el-text>
            <div style="display: block;width: 100%;clear: both">
              <el-text type="warning" v-if="srcStatus">正在分发到服务器</el-text>
              <el-text type="warning" v-else-if="task.remoteProgress">正在分发到服务器{{
                  task.remoteProgress
                }}%
              </el-text>
            </div>
          </el-row>
          <el-progress v-if="!srcStatus"
                       :percentage="task.progress"
                       :status="task.progress === 100 ? 'success' : task.status"
                       :stroke-width="16"
          />
          <el-progress v-else
                       :percentage="task.remoteProgress"
                       :status="task.remoteProgress === 100 ? 'success' : task.status"
                       :stroke-width="16"
          />
        </div>
      </el-scrollbar>
    </el-card>
  </div>
</template>

<style scoped lang="scss">
.empty-container {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 150px 0;
}

.empty-icon {
  margin-bottom: -20px;
}

.empty-text {
  color: #909399;
  font-size: 14px;
}

.progress-window {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 500px;
  z-index: 9999;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.progress-item {
  margin-bottom: 16px;
}

.progress-header {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 12px;
}

.filename {
  font-weight: 500;
  margin-right: 8px;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hostname {
  margin-right: auto;
}

.speed {
  margin-left: 12px;
  font-size: 0.9em;
}

.space-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 24px;
}

.public {
  background: rgb(255, 231, 186);
  border-radius: 4px;
  color: rgb(51, 51, 51);
  margin-right: 4px;
  padding: 0 8px;
}

.private {
  background: rgb(186, 231, 255);
  border-radius: 4px;
  color: rgb(51, 51, 51);
  margin-right: 4px;
  padding: 0 8px;
}

.selected-row {
  background-color: #398dff; /* 选中行的背景色 */
}
</style>
